package ro.gateway.aida.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;

import ro.gateway.aida.srv.IConfigurator;
import ro.gateway.aida.srv.PropertiesConfigurator;


/**
 * <p>Title: Romanian AIDA</p> <p>Description: :D application</p> <p>Copyright: Copyright (comparator) 2003</p>
 * <p>Company: Romania Development Gateway </p>
 *
 * @author Mihai Popoaei, mihai_popoaei@yahoo.com, smike@intellisource.ro
 * @version 1.0-* @version $Id: SimpleDBPool.java,v 1.2 2005/04/04 00:09:01 mihaipostelnicu Exp $
 */
public class SimpleDBPool implements IDBPool {

    private boolean configured = false;
    private Vector availConnections;
    private Vector inUseConnection;
    private int maxConnections;
    private long MAX_USE_TIME = 50000; // 50000
    private long CHECK_CONNECTIONS_TIME = 100000; // 100000
    private long LAST_CHECK_TIME = 0;

    private String db_driver;
    private String db_url;
    private String db_username;
    private String db_password;

    public SimpleDBPool (DBConnector c) {
        this( 25 );
        db_driver=c.getDriverClass();
        db_url=c.createURL();
        db_username=c.getUser();
        db_password=c.getPassword();
    }

    public SimpleDBPool ( int maxConnections ) {
        this( maxConnections, 50000 );
    }

    public SimpleDBPool ( int maxConnections, long max_use_time ) {
        this.availConnections = new Vector();
        this.inUseConnection = new Vector();
        this.configured = false;
        this.maxConnections = maxConnections;
        this.MAX_USE_TIME = max_use_time;
    }

    /**
     * Returneaza prima conexiune din vectorul de conexiuni. Aceasta este stearsa, iar in momentul in care nu mai este
     * folosita este apelata addConnection de mai jos.
     *
     * @return
     */
    public Connection getConnection () {
        ConnectionWrapper result = null;


        long current_time = System.currentTimeMillis();
        if ( current_time - LAST_CHECK_TIME > CHECK_CONNECTIONS_TIME ) {
            checkInUseConnections();
        }

        if ( availConnections.size() > 0 ) {
            result = ( ConnectionWrapper ) availConnections.remove( 0 );
            inUseConnection.add( result );
            if ( ( DEBUG_MASK & DEBUG_SHOW_GETCON_INFO ) == DEBUG_SHOW_GETCON_INFO ) {
                System.out.println( "\tReturned from avail connections" );
            }
        } else {
            if ( inUseConnection.size() > maxConnections ) {
                // verifica daca sint conexiuni
                // in folosinta de f. mult timp
                checkInUseConnections();
            }
            if ( inUseConnection.size() > maxConnections ) {
                return null;
            }

            // adaug o noua conexiune
            result = createConnection();
            inUseConnection.add( result );
            if ( ( DEBUG_MASK & DEBUG_SHOW_GETCON_INFO ) == DEBUG_SHOW_GETCON_INFO ) {
                System.out.println( "\tReturned a new connection" );
            }
        }

        try {
            if ( result.isClosed() || !result.isOK() ) {
                if ( ( DEBUG_MASK & DEBUG_SHOW_GETCON_INFO ) == DEBUG_SHOW_GETCON_INFO ) {
                    System.out.println( "trying to give a closed connection: ... repairing ..." );
                }
                availConnections.remove( result );
                inUseConnection.remove( result );
                result = createConnection();
                inUseConnection.add( result );
            }
        } catch ( SQLException sqlex ) {
            sqlex.printStackTrace();
        }

        if ( ( DEBUG_MASK & DEBUG_SHOW_NR_CONNECTIONS ) == DEBUG_SHOW_NR_CONNECTIONS ) {
            System.out.println( "\t Connections: " + inUseConnection.size() + " in use, " +
                availConnections.size() + " available" );
        }

        result.lastUse = System.currentTimeMillis();
        return result;
    }

    public ConnectionWrapper createConnection () {
        Connection result = null;

        try {
            Class.forName( this.db_driver );
            System.out.println( "Creating connection: db_driver=" + db_driver + " db_url=" +
                db_url + " " + inUseConnection.size() + " in use, " + availConnections.size() + " avail." );
            result = DriverManager.getConnection( db_url, db_username,
                db_password );
        } catch ( SQLException sqlEx ) {
            sqlEx.printStackTrace();
            System.err.println( "connection creation error -> not configured properly?" );
            configured = false;
        } catch ( ClassNotFoundException cnfEx ) {
            cnfEx.printStackTrace();
            System.err.println( "connection creation error (driver not found) -> not configured properly?" );
            configured = false;
        }

        System.out.println( "connection created" );
        return new ConnectionWrapper( this, result );
    }

    /**
     * Verific daca printre conexiunile date spre folosinta sint care au depasit dimpul MAX_USE_TIME
     * <p/>
     * Daca sint le distrug.
     */
    private void checkInUseConnections () {
        long currentTime = System.currentTimeMillis();
        LAST_CHECK_TIME = currentTime;
        for ( java.util.Enumeration e = inUseConnection.elements();
              e.hasMoreElements(); ) {

            ConnectionWrapper con = ( ConnectionWrapper ) e.nextElement();
            if ( ( currentTime - con.lastUse ) > MAX_USE_TIME ) {
                // bad connection use
                // rarely, I hope :D
                System.out.println( "\tWARNING: Connection not closed:" + con );
                con.printLastStatements();
                System.out.println( currentTime - con.lastUse + " / " + MAX_USE_TIME );
                inUseConnection.remove( con );
                try {
                    con.realyClose();
                } catch ( SQLException sqlEx ) {
                    System.err.println( "\n\nIN: checkInUseConnections" );
                    sqlEx.printStackTrace();
                }
                continue;
            }
        }
    }


    /**
     * Apelata de ConnectionWrapper in momentul in care se "inchide" conexiunea
     *
     * @param connection
     */
    public void connectionAvailable ( Connection connection ) {
        if ( availConnections.contains( connection ) ) {
            System.out.println( "\tBUGGY: Connection released twice!! Removing connection (buggy component)" );
            ( ( ConnectionWrapper ) connection ).printLastStatements();
            availConnections.remove( connection );
            inUseConnection.remove( connection );
            return;
        }
        if ( ( DEBUG_MASK & DEBUG_SHOW_RELEASING_INFO ) == DEBUG_SHOW_RELEASING_INFO ) {
            System.out.println( "\tCon back to pool:" + connection );
        }

        if ( !inUseConnection.remove( connection ) ) {
            System.out.println( "\tWARNING: Try to give back a wrong connection:" + connection );
        } else {
            availConnections.add( connection );
        }
    }

    /**
     * Suporta PropertiesConfigurator cu atributele: db_driver db_url db_username db_password
     *
     * @param configurator
     */
    public void configure ( IConfigurator configurator ) {
        if ( configured ) {
            throw new RuntimeException( "Illegal call: SimpleDBPool already configured" );
        }

        if ( configurator instanceof PropertiesConfigurator ) {
            configure( ( PropertiesConfigurator ) configurator );
        } else {
            throw new RuntimeException( "Illegal call: configurator not supported" );
        }
    }

    private void configure ( PropertiesConfigurator configurator ) {
        this.db_url = configurator.properties.getProperty( "db_url" );
        this.db_username = configurator.properties.getProperty( "db_username" );
        this.db_password = configurator.properties.getProperty( "db_password" );
        this.db_driver = configurator.properties.getProperty( "db_driver" );
        System.out.println( "DBPool configured properly" );
    }

    public void release () {
        for ( java.util.Enumeration e = availConnections.elements();
              e.hasMoreElements(); ) {

            ConnectionWrapper con = ( ConnectionWrapper ) e.nextElement();
            try {
                con.realyClose();
            } catch ( SQLException sqlEx ) {
                sqlEx.printStackTrace();
            }
        }

        for ( java.util.Enumeration e = inUseConnection.elements();
              e.hasMoreElements(); ) {

            ConnectionWrapper con = ( ConnectionWrapper ) e.nextElement();
            try {
                con.realyClose();
            } catch ( SQLException sqlEx ) {
                sqlEx.printStackTrace();
            }
        }
    }

    public static int DEBUG_SHOW_NR_CONNECTIONS = 1;
    public static int DEBUG_SHOW_RELEASING_INFO = 2;
    public static int DEBUG_SHOW_GETCON_INFO = 4;
//     public static int DEBUG_MASK =
//        DEBUG_SHOW_NR_CONNECTIONS
//        | DEBUG_SHOW_RELEASING_INFO
//        | DEBUG_SHOW_GETCON_INFO;
    public static int DEBUG_MASK = 0;
}